library(dplyr)
library(ggplot2)
library(plotly)
data_file <- read.csv("cleaned_online_retail.csv")
sample(data_file)
str(data_file)
'data.frame':   512974 obs. of  14 variables:
 $ Invoice            : chr  "489434" "489434" "489434" "489434" ...
 $ StockCode          : chr  "85048" "79323P" "79323W" "22041" ...
 $ Description        : chr  "15CM CHRISTMAS GLASS BALL 20 LIGHTS" "PINK CHERRY LIGHTS" "WHITE CHERRY LIGHTS" "RECORD FRAME 7\" SINGLE SIZE" ...
 $ Quantity           : int  12 12 12 48 24 24 24 10 12 12 ...
 $ InvoiceDate        : chr  "2009-12-01 07:45:00" "2009-12-01 07:45:00" "2009-12-01 07:45:00" "2009-12-01 07:45:00" ...
 $ Price              : num  6.95 6.75 6.75 2.1 1.25 1.65 1.25 5.95 2.55 3.75 ...
 $ Customer.ID        : int  13085 13085 13085 13085 13085 13085 13085 13085 13085 13085 ...
 $ Country            : chr  "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
 $ TotalPrice         : num  83.4 81 81 100.8 30 ...
 $ z_score            : num  0.0155 0.0141 0.0141 -0.0177 -0.0235 ...
 $ z_score_quantity   : num  0.00302 0.00302 0.00302 0.39017 0.13207 ...
 $ z_score_total_price: num  0.976 0.939 0.939 1.242 0.16 ...
 $ Quantity_capped    : int  12 12 12 32 24 24 24 10 12 12 ...
 $ TotalPrice_capped  : num  60 60 60 60 30 39.6 30 59.5 30.6 45 ...
# Convert relevant columns to factors
data_file$Country <- as.factor(data_file$Country)
data_file$StockCode <- as.factor(data_file$StockCode)

# Converting integer to numeric
data_file$Quantity <- as.numeric(data_file$Quantity)
names(data_file)
 [1] "Invoice"             "StockCode"          
 [3] "Description"         "Quantity"           
 [5] "InvoiceDate"         "Price"              
 [7] "Customer.ID"         "Country"            
 [9] "TotalPrice"          "z_score"            
[11] "z_score_quantity"    "z_score_total_price"
[13] "Quantity_capped"     "TotalPrice_capped"  
# removing the unnecessary columns from the cleaned dataset
visualize_df <- data_file %>% 
  select(Invoice, StockCode, Description, Quantity, InvoiceDate, Price, TotalPrice, Customer.ID, Country)
visualize_df <- write.csv(visualize_df, "visualize_df.csv", row.names = FALSE)
visualize_df <- read.csv("visualize_df.csv")
head(visualize_df)
summary(visualize_df)
   Invoice           StockCode         Description       
 Length:512974      Length:512974      Length:512974     
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
    Quantity        InvoiceDate            Price        
 Min.   :    1.00   Length:512974      Min.   :  0.000  
 1st Qu.:    1.00   Class :character   1st Qu.:  1.250  
 Median :    3.00   Mode  :character   Median :  2.100  
 Mean   :   11.72                      Mean   :  3.643  
 3rd Qu.:   11.00                      3rd Qu.:  4.210  
 Max.   :19152.00                      Max.   :441.100  
                                                        
   TotalPrice        Customer.ID       Country         
 Min.   :    0.00   Min.   :12346    Length:512974     
 1st Qu.:    3.90   1st Qu.:13997    Class :character  
 Median :   10.12   Median :15321    Mode  :character  
 Mean   :   19.49   Mean   :15369                      
 3rd Qu.:   17.70   3rd Qu.:16814                      
 Max.   :15818.40   Max.   :18287                      
                    NA's   :105320                     

DATA VISUALIZATION:

# Visualizing the distribution of Quantity
vis_quantity <- ggplot(visualize_df, aes(log(Quantity+1))) +
  geom_histogram(bins = 50, fill = "blue", color = "white") +
  labs(title = "Distribution of Quantity Sold", x = "Quantity", y = "Density")
ggplotly(vis_quantity)

Observation: The histogram shows a skewed distribution, indicating that most products are sold in small quantities, with a few products having significantly higher quantities.

Interpretation: The majority of items in the store are sold in small volumes, which might be typical for a wide-ranging product catalog. However, there are occasional bulk purchases, reflected in the long tail of the distribution.

# Visualizing the distribution of Price
vis_price <- ggplot(visualize_df, aes(log(Price+1))) +
  geom_histogram(bins = 50, fill = "blue", color = "white") +
  labs(title = "Distribution of Product Prices", x = "Price", y = "Frequency")
ggplotly(vis_price)

Observation: The distribution of product prices reveals that most items are priced lower, with few high-priced products.

Interpretation: The store mainly sells low-priced items, and higher-priced items are outliers, making up a small portion of the total products offered.

# Visualizing the distribution of TotalPrice (Revenue)
vis_total_price <- ggplot(visualize_df, aes(x = log(TotalPrice+1))) +
  geom_histogram(bins = 50, fill = "blue", color = "white") +
  labs(title = "Distribution of Total Transaction Values (TotalPrice)", x = "TotalPrice", y = "Frequency") + 
  theme_classic()
ggplotly(vis_total_price)

Observation: Most transactions fall in the lower range of total price, with only a few transactions generating higher total revenue.

Interpretation: Similar to individual product prices, total transaction amounts are concentrated around lower values, reflecting frequent small purchases and occasional larger sales.

# Density plot for Quantity
vis_qunatity_dens <- ggplot(visualize_df, aes(log(Quantity)+1)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Quantity Sold", x = "Quantity", y = "Density")
ggplotly(vis_qunatity_dens)

Observation: The density plot highlights the peak at lower quantities sold.

Interpretation: The majority of sales involve fewer units, suggesting a high frequency of single or small quantity purchases.

# Density plot for Price
vis_price_dens <- ggplot(visualize_df, aes(log(Price+1))) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Product Prices", x = "Price", y = "Density")
ggplotly(vis_price_dens)

Observation: The majority of products are priced in the lower range, with a smooth decline toward higher-priced products.

Interpretation: Most items are inexpensive, reflecting a retail strategy focused on affordability, with a few higher-priced items possibly driving luxury or special purchases.

# Density plot for TotalPrice
vis_total_price_dens <- ggplot(visualize_df, aes(log(TotalPrice)+1)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Total Transaction Values (TotalPrice)", x = "TotalPrice", y = "Density")

ggplotly(vis_total_price_dens)
Warning: Removed 1566 rows containing non-finite outside the scale
range (`stat_density()`).

Observation: The density plot for total transaction values shows that most transactions involve lower revenue.

Interpretation: Customers tend to make smaller purchases more frequently, contributing to the overall sales pattern of the store.

CUSTOMER ANALYSIS

# Top 10 customers by total spending
top_customers <- visualize_df %>%
  group_by(Customer.ID) %>%
  summarise(TotalSpent = sum(TotalPrice)) %>%
  arrange(desc(TotalSpent)) %>%
  head(10)
  
# Plot Top 10 Customers by Total Spending
vis_top_custmers <- ggplot(top_customers, aes(x = reorder(Customer.ID, TotalSpent), y = TotalSpent)) +
  geom_bar(stat = "identity", fill = "blue") +
  coord_flip() +
  labs(title = "Top 10 Customers by Total Spending", x = "Customer ID", y = "Total Spending") + 
  theme_classic()

# Convert to interactive plot using ggplotly
ggplotly(vis_top_custmers)
NA

Observation: The top 10 customers contribute a significant portion of the total revenue.

Interpretation: A small number of high-value customers are responsible for a large share of sales, which could indicate the importance of customer retention and loyalty programs.

PRODUCT ANALYSIS

# Top 10 products by quantity sold
top_products_quantity <- visualize_df %>%
  group_by(StockCode, Description) %>%
  summarise(TotalQuantity = sum(Quantity), .groups = "drop") %>%
  arrange(desc(TotalQuantity)) %>%
  head(10)

# Plot Top 10 Products by Quantity Sold
vis_top_products_quantity <- ggplot(top_products_quantity, aes(x = reorder(Description, TotalQuantity), y = TotalQuantity)) +
  geom_bar(stat = "identity", fill = "yellow") +
  coord_flip() +
  labs(title = "Top 10 Products by Quantity Sold", x = "Product Description", y = "Total Quantity Sold") +
  theme_classic()

# Convert to interactive plot using ggplotly
ggplotly(vis_top_products_quantity)

Observation: A small set of products make up a large proportion of the total quantity sold.

Observation: A few products contribute disproportionately to the total revenue.

Interpretation: These high-revenue items are the store’s key drivers of profitability and could be a focus for marketing or promotions to increase sales.

# Total sales by country
sales_by_country <- visualize_df %>%
  group_by(Country) %>%
  summarise(TotalSales = sum(TotalPrice))

# Top 10 countries by total sales
top_countries <- sales_by_country %>%
  arrange(desc(TotalSales)) %>%
  head(10)

# Plot Top 10 Countries by Total Sales
top_countries <- ggplot(top_countries, aes(x = reorder(Country, TotalSales), y = TotalSales)) +
  geom_bar(stat = "identity", fill = "red") +
  coord_flip() +
  labs(title = "Top 10 Countries by Total Sales", x = "Country", y = "Total Sales") +
  theme_classic()

# Convert to interactive plot using ggplotly
ggplotly(top_countries)
NA

Observation: The majority of sales are concentrated in a small number of countries.

Interpretation: The store likely operates in key markets where it has established strong customer bases, but may have opportunities to expand sales in underrepresented regions.

TIME SERIES ANALYSIS

# Convert InvoiceDate from character to POSIXct format
visualize_df$InvoiceDate <- as.POSIXct(visualize_df$InvoiceDate, format = "%Y-%m-%d %H:%M:%S")

# Extract just the date (without time) for daily aggregation
visualize_df$DateOnly <- as.Date(visualize_df$InvoiceDate)

# Aggregate the data by day to calculate total sales per day
daily_sales <- visualize_df %>%
  group_by(DateOnly) %>%
  summarise(TotalSales = sum(TotalPrice))

# Take a look at the first few rows
head(daily_sales)
# Create a line plot of total sales over time (daily)
vis_date_daily <- ggplot(daily_sales, aes(x = DateOnly, y = TotalSales)) +
  geom_line(color = "green", linewidth = 0.5) +
  labs(title = "Total Sales Over Time (Daily)", x = "Date", y = "Total Sales (Revenue)") +
  theme_classic()

# Convert to an interactive plot using ggplotly
ggplotly(vis_date_daily)
NA

Observation: The plot shows fluctuations in daily sales, with noticeable peaks during certain periods.

Interpretation: The data indicates periods of steady growth and possible seasonal spikes, useful for forecasting future sales and managing inventory.

# Create a line plot of total sales over time (daily)
vis_date_daily_sm <- ggplot(daily_sales, aes(x = DateOnly, y = TotalSales)) +
  geom_line(color = "green", size = 0.5) +
  geom_smooth(method = "loess", color = "darkgreen", size = 0.3) +
  labs(title = "Total Sales Over Time (Daily)", x = "Date", y = "Total Sales (Revenue)") +
  theme_classic()

# Convert to an interactive plot using ggplotly
ggplotly(vis_date_daily_sm)
`geom_smooth()` using formula = 'y ~ x'

Observation: The line plot with the smoothing line shows fluctuations in daily sales, with some distinct peaks.

Interpretation: The peaks likely correspond to high sales days, possibly driven by promotional events or holidays. The smooth line provides an overall trend, showing growth or decline patterns in sales over time.

# Extract the day of the week (e.g., Sunday, Monday)
visualize_df$DayOfWeek <- weekdays(visualize_df$DateOnly)

# Aggregate data by day of the week to calculate total traffic (quantity sold)
weekly_traffic <- visualize_df %>%
  group_by(DayOfWeek) %>%
  summarise(TotalTraffic = sum(Quantity))

# Ensure the days of the week are in the correct order
days_order <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
weekly_traffic$DayOfWeek <- factor(weekly_traffic$DayOfWeek, levels = days_order)

# Print the aggregated weekly traffic data
print(weekly_traffic)
# Create a bar plot to visualize traffic by day of the week
traffic_by_day_plot <- ggplot(weekly_traffic, aes(x = DayOfWeek, y = TotalTraffic, fill = DayOfWeek)) +
  geom_bar(stat = "identity") +  # Bar plot
  labs(title = "Total Traffic (Quantity Sold) by Day of the Week", x = "Day of the Week", y = "Total Traffic (Quantity Sold)") +
  theme_classic() +
  theme(legend.position = "none")

# Convert to an interactive plot using ggplotly
ggplotly(traffic_by_day_plot)
NA

Observation: Sales traffic peaks on certain days of the week, with noticeably higher activity.

Interpretation: Customer behavior shows that specific days drive more sales, which can help in optimizing staffing, promotions, and operations for those high-traffic days.

# Extract the hour of the day from the InvoiceDate
visualize_df$Hour <- format(visualize_df$InvoiceDate, "%H")

# Aggregate data by hour of the day to calculate total sales
hourly_sales <- visualize_df %>%
  group_by(Hour) %>%
  summarise(TotalSales = sum(TotalPrice))

# Plot total sales by hour
vis_sales_by_hour <- ggplot(hourly_sales, aes(x = Hour, y = TotalSales)) +
  geom_bar(stat = "identity", fill = "blue") +
  labs(title = "Total Sales by Hour of Day", x = "Hour", y = "Total Sales (Revenue)") +
  theme_classic()

# Convert to an interactive plot using ggplotly
ggplotly(vis_sales_by_hour)
NA

Observation: Sales activity varies throughout the day, with peaks at certain hours.

Interpretation: This analysis is crucial for understanding peak shopping times, allowing the store to optimize resources, advertising, and customer service during the busiest periods.

# Aggregate sales by date and country
sales_by_country_over_time <- visualize_df %>%
  group_by(DateOnly, Country) %>%
  summarise(TotalSales = sum(TotalPrice), .groups = "drop")

# Filter to top 5 countries for better comparison
top_5_countries <- visualize_df %>%
  group_by(Country) %>%
  summarise(TotalSales = sum(TotalPrice)) %>%
  arrange(desc(TotalSales)) %>%
  head(5)

# Filter data for the top 5 countries
sales_top_5_countries <- sales_by_country_over_time %>%
  filter(Country %in% top_5_countries$Country)

# Plot sales trends by country over time
vis_sales_by_country_time <- ggplot(sales_top_5_countries, aes(x = DateOnly, y = TotalSales, color = Country)) +
  geom_line() +
  labs(title = "Sales Trends Over Time by Country", x = "Date", y = "Total Sales (Revenue)") +
  theme_minimal()

# Convert to interactive plot using ggplotly
ggplotly(vis_sales_by_country_time)
NA

Observation: The majority of customers are low spenders, with fewer medium and high spenders.

Interpretation: The store has a broad customer base, but most of the revenue is likely coming from a small number of high-spending customers. Retaining high spenders and converting more low spenders to medium or high spenders should be a priority.

# Count the number of orders per product (instead of total quantity sold)
top_products_orders <- visualize_df %>%
  group_by(StockCode, Description) %>%
  summarise(OrderCount = n(), .groups = "drop") %>%
  arrange(desc(OrderCount)) %>%
  head(10)

# Plot top products based on number of orders
vis_top_products_orders <- ggplot(top_products_orders, aes(x = reorder(Description, OrderCount), y = OrderCount)) +
  geom_bar(stat = "identity", fill = "orange") +
  coord_flip() +
  labs(title = "Top Products Based on Number of Orders", x = "Product Description", y = "Number of Orders") +
  theme_classic()

# Convert to an interactive plot using ggplotly
ggplotly(vis_top_products_orders)
NA

Observation: A few products are ordered much more frequently than others.

LS0tDQp0aXRsZTogImRhdGEgdmlzdWFsaXphdGlvbiBmb3IgcmV0YWlsIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YV9maWxlIDwtIHJlYWQuY3N2KCJjbGVhbmVkX29ubGluZV9yZXRhaWwuY3N2IikNCmBgYA0KDQpgYGB7cn0NCnNhbXBsZShkYXRhX2ZpbGUpDQpgYGANCg0KYGBge3J9DQpzdHIoZGF0YV9maWxlKQ0KYGBgDQpgYGB7cn0NCiMgQ29udmVydCByZWxldmFudCBjb2x1bW5zIHRvIGZhY3RvcnMNCmRhdGFfZmlsZSRDb3VudHJ5IDwtIGFzLmZhY3RvcihkYXRhX2ZpbGUkQ291bnRyeSkNCmRhdGFfZmlsZSRTdG9ja0NvZGUgPC0gYXMuZmFjdG9yKGRhdGFfZmlsZSRTdG9ja0NvZGUpDQoNCiMgQ29udmVydGluZyBpbnRlZ2VyIHRvIG51bWVyaWMNCmRhdGFfZmlsZSRRdWFudGl0eSA8LSBhcy5udW1lcmljKGRhdGFfZmlsZSRRdWFudGl0eSkNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGRhdGFfZmlsZSkNCmBgYA0KDQpgYGB7cn0NCiMgcmVtb3ZpbmcgdGhlIHVubmVjZXNzYXJ5IGNvbHVtbnMgZnJvbSB0aGUgY2xlYW5lZCBkYXRhc2V0DQp2aXN1YWxpemVfZGYgPC0gZGF0YV9maWxlICU+JSANCiAgc2VsZWN0KEludm9pY2UsIFN0b2NrQ29kZSwgRGVzY3JpcHRpb24sIFF1YW50aXR5LCBJbnZvaWNlRGF0ZSwgUHJpY2UsIFRvdGFsUHJpY2UsIEN1c3RvbWVyLklELCBDb3VudHJ5KQ0KYGBgDQoNCmBgYHtyfQ0KdmlzdWFsaXplX2RmIDwtIHdyaXRlLmNzdih2aXN1YWxpemVfZGYsICJ2aXN1YWxpemVfZGYuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KdmlzdWFsaXplX2RmIDwtIHJlYWQuY3N2KCJ2aXN1YWxpemVfZGYuY3N2IikNCmBgYA0KDQpgYGB7cn0NCmhlYWQodmlzdWFsaXplX2RmKQ0KYGBgDQpgYGB7cn0NCnN1bW1hcnkodmlzdWFsaXplX2RmKQ0KYGBgDQoNCg0KIyMjIERBVEEgVklTVUFMSVpBVElPTjogIyMjDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIFF1YW50aXR5DQp2aXNfcXVhbnRpdHkgPC0gZ2dwbG90KHZpc3VhbGl6ZV9kZiwgYWVzKGxvZyhRdWFudGl0eSsxKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwLCBmaWxsID0gImJsdWUiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUXVhbnRpdHkgU29sZCIsIHggPSAiUXVhbnRpdHkiLCB5ID0gIkRlbnNpdHkiKQ0KZ2dwbG90bHkodmlzX3F1YW50aXR5KQ0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgaGlzdG9ncmFtIHNob3dzIGEgc2tld2VkIGRpc3RyaWJ1dGlvbiwgaW5kaWNhdGluZyB0aGF0IG1vc3QgcHJvZHVjdHMgYXJlIHNvbGQgaW4gc21hbGwgcXVhbnRpdGllcywgd2l0aCBhIGZldyBwcm9kdWN0cyBoYXZpbmcgc2lnbmlmaWNhbnRseSBoaWdoZXIgcXVhbnRpdGllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBtYWpvcml0eSBvZiBpdGVtcyBpbiB0aGUgc3RvcmUgYXJlIHNvbGQgaW4gc21hbGwgdm9sdW1lcywgd2hpY2ggbWlnaHQgYmUgdHlwaWNhbCBmb3IgYSB3aWRlLXJhbmdpbmcgcHJvZHVjdCBjYXRhbG9nLiBIb3dldmVyLCB0aGVyZSBhcmUgb2NjYXNpb25hbCBidWxrIHB1cmNoYXNlcywgcmVmbGVjdGVkIGluIHRoZSBsb25nIHRhaWwgb2YgdGhlIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQojIFZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgUHJpY2UNCnZpc19wcmljZSA8LSBnZ3Bsb3QodmlzdWFsaXplX2RmLCBhZXMobG9nKFByaWNlKzEpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gIndoaXRlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBQcm9kdWN0IFByaWNlcyIsIHggPSAiUHJpY2UiLCB5ID0gIkZyZXF1ZW5jeSIpDQpnZ3Bsb3RseSh2aXNfcHJpY2UpDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBkaXN0cmlidXRpb24gb2YgcHJvZHVjdCBwcmljZXMgcmV2ZWFscyB0aGF0IG1vc3QgaXRlbXMgYXJlIHByaWNlZCBsb3dlciwgd2l0aCBmZXcgaGlnaC1wcmljZWQgcHJvZHVjdHMuDQojIEludGVycHJldGF0aW9uOiBUaGUgc3RvcmUgbWFpbmx5IHNlbGxzIGxvdy1wcmljZWQgaXRlbXMsIGFuZCBoaWdoZXItcHJpY2VkIGl0ZW1zIGFyZSBvdXRsaWVycywgbWFraW5nIHVwIGEgc21hbGwgcG9ydGlvbiBvZiB0aGUgdG90YWwgcHJvZHVjdHMgb2ZmZXJlZC4NCg0KYGBge3J9DQojIFZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgVG90YWxQcmljZSAoUmV2ZW51ZSkNCnZpc190b3RhbF9wcmljZSA8LSBnZ3Bsb3QodmlzdWFsaXplX2RmLCBhZXMoeCA9IGxvZyhUb3RhbFByaWNlKzEpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gIndoaXRlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBUb3RhbCBUcmFuc2FjdGlvbiBWYWx1ZXMgKFRvdGFsUHJpY2UpIiwgeCA9ICJUb3RhbFByaWNlIiwgeSA9ICJGcmVxdWVuY3kiKSArIA0KICB0aGVtZV9jbGFzc2ljKCkNCmdncGxvdGx5KHZpc190b3RhbF9wcmljZSkNCmBgYA0KIyBPYnNlcnZhdGlvbjogTW9zdCB0cmFuc2FjdGlvbnMgZmFsbCBpbiB0aGUgbG93ZXIgcmFuZ2Ugb2YgdG90YWwgcHJpY2UsIHdpdGggb25seSBhIGZldyB0cmFuc2FjdGlvbnMgZ2VuZXJhdGluZyBoaWdoZXIgdG90YWwgcmV2ZW51ZS4NCiMgSW50ZXJwcmV0YXRpb246IFNpbWlsYXIgdG8gaW5kaXZpZHVhbCBwcm9kdWN0IHByaWNlcywgdG90YWwgdHJhbnNhY3Rpb24gYW1vdW50cyBhcmUgY29uY2VudHJhdGVkIGFyb3VuZCBsb3dlciB2YWx1ZXMsIHJlZmxlY3RpbmcgZnJlcXVlbnQgc21hbGwgcHVyY2hhc2VzIGFuZCBvY2Nhc2lvbmFsIGxhcmdlciBzYWxlcy4NCg0KYGBge3J9DQojIERlbnNpdHkgcGxvdCBmb3IgUXVhbnRpdHkNCnZpc19xdW5hdGl0eV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coUXVhbnRpdHkpKzEpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBRdWFudGl0eSBTb2xkIiwgeCA9ICJRdWFudGl0eSIsIHkgPSAiRGVuc2l0eSIpDQpnZ3Bsb3RseSh2aXNfcXVuYXRpdHlfZGVucykNCmBgYA0KIyBPYnNlcnZhdGlvbjogVGhlIGRlbnNpdHkgcGxvdCBoaWdobGlnaHRzIHRoZSBwZWFrIGF0IGxvd2VyIHF1YW50aXRpZXMgc29sZC4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBtYWpvcml0eSBvZiBzYWxlcyBpbnZvbHZlIGZld2VyIHVuaXRzLCBzdWdnZXN0aW5nIGEgaGlnaCBmcmVxdWVuY3kgb2Ygc2luZ2xlIG9yIHNtYWxsIHF1YW50aXR5IHB1cmNoYXNlcy4NCg0KYGBge3J9DQojIERlbnNpdHkgcGxvdCBmb3IgUHJpY2UNCnZpc19wcmljZV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coUHJpY2UrMSkpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBQcm9kdWN0IFByaWNlcyIsIHggPSAiUHJpY2UiLCB5ID0gIkRlbnNpdHkiKQ0KZ2dwbG90bHkodmlzX3ByaWNlX2RlbnMpDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBwcm9kdWN0cyBhcmUgcHJpY2VkIGluIHRoZSBsb3dlciByYW5nZSwgd2l0aCBhIHNtb290aCBkZWNsaW5lIHRvd2FyZCBoaWdoZXItcHJpY2VkIHByb2R1Y3RzLg0KIyBJbnRlcnByZXRhdGlvbjogTW9zdCBpdGVtcyBhcmUgaW5leHBlbnNpdmUsIHJlZmxlY3RpbmcgYSByZXRhaWwgc3RyYXRlZ3kgZm9jdXNlZCBvbiBhZmZvcmRhYmlsaXR5LCB3aXRoIGEgZmV3IGhpZ2hlci1wcmljZWQgaXRlbXMgcG9zc2libHkgZHJpdmluZyBsdXh1cnkgb3Igc3BlY2lhbCBwdXJjaGFzZXMuDQoNCmBgYHtyfQ0KIyBEZW5zaXR5IHBsb3QgZm9yIFRvdGFsUHJpY2UNCnZpc190b3RhbF9wcmljZV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coVG90YWxQcmljZSkrMSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFRvdGFsIFRyYW5zYWN0aW9uIFZhbHVlcyAoVG90YWxQcmljZSkiLCB4ID0gIlRvdGFsUHJpY2UiLCB5ID0gIkRlbnNpdHkiKQ0KDQpnZ3Bsb3RseSh2aXNfdG90YWxfcHJpY2VfZGVucykNCmBgYA0KIyBPYnNlcnZhdGlvbjogVGhlIGRlbnNpdHkgcGxvdCBmb3IgdG90YWwgdHJhbnNhY3Rpb24gdmFsdWVzIHNob3dzIHRoYXQgbW9zdCB0cmFuc2FjdGlvbnMgaW52b2x2ZSBsb3dlciByZXZlbnVlLg0KIyBJbnRlcnByZXRhdGlvbjogQ3VzdG9tZXJzIHRlbmQgdG8gbWFrZSBzbWFsbGVyIHB1cmNoYXNlcyBtb3JlIGZyZXF1ZW50bHksIGNvbnRyaWJ1dGluZyB0byB0aGUgb3ZlcmFsbCBzYWxlcyBwYXR0ZXJuIG9mIHRoZSBzdG9yZS4NCg0KDQojIyMgQ1VTVE9NRVIgQU5BTFlTSVMgIyMjDQogIA0KYGBge3J9DQojIFRvcCAxMCBjdXN0b21lcnMgYnkgdG90YWwgc3BlbmRpbmcNCnRvcF9jdXN0b21lcnMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDdXN0b21lci5JRCkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNwZW50ID0gc3VtKFRvdGFsUHJpY2UpKSAlPiUNCiAgYXJyYW5nZShkZXNjKFRvdGFsU3BlbnQpKSAlPiUNCiAgaGVhZCgxMCkNCiAgDQojIFBsb3QgVG9wIDEwIEN1c3RvbWVycyBieSBUb3RhbCBTcGVuZGluZw0KdmlzX3RvcF9jdXN0bWVycyA8LSBnZ3Bsb3QodG9wX2N1c3RvbWVycywgYWVzKHggPSByZW9yZGVyKEN1c3RvbWVyLklELCBUb3RhbFNwZW50KSwgeSA9IFRvdGFsU3BlbnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsdWUiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDEwIEN1c3RvbWVycyBieSBUb3RhbCBTcGVuZGluZyIsIHggPSAiQ3VzdG9tZXIgSUQiLCB5ID0gIlRvdGFsIFNwZW5kaW5nIikgKyANCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX2N1c3RtZXJzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSB0b3AgMTAgY3VzdG9tZXJzIGNvbnRyaWJ1dGUgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIHRoZSB0b3RhbCByZXZlbnVlLg0KIyBJbnRlcnByZXRhdGlvbjogQSBzbWFsbCBudW1iZXIgb2YgaGlnaC12YWx1ZSBjdXN0b21lcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIHNoYXJlIG9mIHNhbGVzLCB3aGljaCBjb3VsZCBpbmRpY2F0ZSB0aGUgaW1wb3J0YW5jZSBvZiBjdXN0b21lciByZXRlbnRpb24gYW5kIGxveWFsdHkgcHJvZ3JhbXMuDQoNCg0KIyMjIFBST0RVQ1QgQU5BTFlTSVMgIyMjDQpgYGB7cn0NCiMgVG9wIDEwIHByb2R1Y3RzIGJ5IHF1YW50aXR5IHNvbGQNCnRvcF9wcm9kdWN0c19xdWFudGl0eSA8LSB2aXN1YWxpemVfZGYgJT4lDQogIGdyb3VwX2J5KFN0b2NrQ29kZSwgRGVzY3JpcHRpb24pICU+JQ0KICBzdW1tYXJpc2UoVG90YWxRdWFudGl0eSA9IHN1bShRdWFudGl0eSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBhcnJhbmdlKGRlc2MoVG90YWxRdWFudGl0eSkpICU+JQ0KICBoZWFkKDEwKQ0KDQojIFBsb3QgVG9wIDEwIFByb2R1Y3RzIGJ5IFF1YW50aXR5IFNvbGQNCnZpc190b3BfcHJvZHVjdHNfcXVhbnRpdHkgPC0gZ2dwbG90KHRvcF9wcm9kdWN0c19xdWFudGl0eSwgYWVzKHggPSByZW9yZGVyKERlc2NyaXB0aW9uLCBUb3RhbFF1YW50aXR5KSwgeSA9IFRvdGFsUXVhbnRpdHkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInllbGxvdyIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgUHJvZHVjdHMgYnkgUXVhbnRpdHkgU29sZCIsIHggPSAiUHJvZHVjdCBEZXNjcmlwdGlvbiIsIHkgPSAiVG90YWwgUXVhbnRpdHkgU29sZCIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX3Byb2R1Y3RzX3F1YW50aXR5KQ0KYGBgDQojIE9ic2VydmF0aW9uOiBBIHNtYWxsIHNldCBvZiBwcm9kdWN0cyBtYWtlIHVwIGEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgdG90YWwgcXVhbnRpdHkgc29sZC4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIHByb2R1Y3RzIGFyZSBsaWtlbHkgcG9wdWxhciBvciBmcmVxdWVudGx5IHB1cmNoYXNlZCBpbiBidWxrLCBpbmRpY2F0aW5nIHRoZWlyIGltcG9ydGFuY2UgaW4gZHJpdmluZyBzYWxlcyB2b2x1bWUgZm9yIHRoZSBzdG9yZS4NCg0KYGBge3J9DQojIFRvcCAxMCBwcm9kdWN0cyBieSB0b3RhbCByZXZlbnVlIChUb3RhbFByaWNlKQ0KdG9wX3Byb2R1Y3RzX3JldmVudWUgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShTdG9ja0NvZGUsIERlc2NyaXB0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsUmV2ZW51ZSA9IHN1bShUb3RhbFByaWNlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyhUb3RhbFJldmVudWUpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IFRvcCAxMCBQcm9kdWN0cyBieSBUb3RhbCBSZXZlbnVlDQp2aXNfdG9wX3Byb2R1Y3RfcmV2ZW51ZSA8LSBnZ3Bsb3QodG9wX3Byb2R1Y3RzX3JldmVudWUsIGFlcyh4ID0gcmVvcmRlcihEZXNjcmlwdGlvbiwgVG90YWxSZXZlbnVlKSwgeSA9IFRvdGFsUmV2ZW51ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAicHVycGxlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCAxMCBQcm9kdWN0cyBieSBSZXZlbnVlIiwgeCA9ICJQcm9kdWN0IERlc2NyaXB0aW9uIiwgeSA9ICJUb3RhbCBSZXZlbnVlIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBDb252ZXJ0IHRvIGludGVyYWN0aXZlIHBsb3QgdXNpbmcgZ2dwbG90bHkNCmdncGxvdGx5KHZpc190b3BfcHJvZHVjdF9yZXZlbnVlKQ0KYGBgDQojIE9ic2VydmF0aW9uOiBBIGZldyBwcm9kdWN0cyBjb250cmlidXRlIGRpc3Byb3BvcnRpb25hdGVseSB0byB0aGUgdG90YWwgcmV2ZW51ZS4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIGhpZ2gtcmV2ZW51ZSBpdGVtcyBhcmUgdGhlIHN0b3JlJ3Mga2V5IGRyaXZlcnMgb2YgcHJvZml0YWJpbGl0eSBhbmQgY291bGQgYmUgYSBmb2N1cyBmb3IgbWFya2V0aW5nIG9yIHByb21vdGlvbnMgdG8gaW5jcmVhc2Ugc2FsZXMuDQoNCmBgYHtyfQ0KIyBUb3RhbCBzYWxlcyBieSBjb3VudHJ5DQpzYWxlc19ieV9jb3VudHJ5IDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpKQ0KDQojIFRvcCAxMCBjb3VudHJpZXMgYnkgdG90YWwgc2FsZXMNCnRvcF9jb3VudHJpZXMgPC0gc2FsZXNfYnlfY291bnRyeSAlPiUNCiAgYXJyYW5nZShkZXNjKFRvdGFsU2FsZXMpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IFRvcCAxMCBDb3VudHJpZXMgYnkgVG90YWwgU2FsZXMNCnRvcF9jb3VudHJpZXMgPC0gZ2dwbG90KHRvcF9jb3VudHJpZXMsIGFlcyh4ID0gcmVvcmRlcihDb3VudHJ5LCBUb3RhbFNhbGVzKSwgeSA9IFRvdGFsU2FsZXMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgQ291bnRyaWVzIGJ5IFRvdGFsIFNhbGVzIiwgeCA9ICJDb3VudHJ5IiwgeSA9ICJUb3RhbCBTYWxlcyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh0b3BfY291bnRyaWVzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBzYWxlcyBhcmUgY29uY2VudHJhdGVkIGluIGEgc21hbGwgbnVtYmVyIG9mIGNvdW50cmllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBzdG9yZSBsaWtlbHkgb3BlcmF0ZXMgaW4ga2V5IG1hcmtldHMgd2hlcmUgaXQgaGFzIGVzdGFibGlzaGVkIHN0cm9uZyBjdXN0b21lciBiYXNlcywgYnV0IG1heSBoYXZlIG9wcG9ydHVuaXRpZXMgdG8gZXhwYW5kIHNhbGVzIGluIHVuZGVycmVwcmVzZW50ZWQgcmVnaW9ucy4NCg0KDQoNCiMjIyBUSU1FIFNFUklFUyBBTkFMWVNJUyANCg0KYGBge3J9DQojIENvbnZlcnQgSW52b2ljZURhdGUgZnJvbSBjaGFyYWN0ZXIgdG8gUE9TSVhjdCBmb3JtYXQNCnZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSA8LSBhcy5QT1NJWGN0KHZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSwgZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNOiVTIikNCg0KIyBFeHRyYWN0IGp1c3QgdGhlIGRhdGUgKHdpdGhvdXQgdGltZSkgZm9yIGRhaWx5IGFnZ3JlZ2F0aW9uDQp2aXN1YWxpemVfZGYkRGF0ZU9ubHkgPC0gYXMuRGF0ZSh2aXN1YWxpemVfZGYkSW52b2ljZURhdGUpDQoNCiMgQWdncmVnYXRlIHRoZSBkYXRhIGJ5IGRheSB0byBjYWxjdWxhdGUgdG90YWwgc2FsZXMgcGVyIGRheQ0KZGFpbHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShEYXRlT25seSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpKQ0KDQojIFRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgcm93cw0KaGVhZChkYWlseV9zYWxlcykNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBhIGxpbmUgcGxvdCBvZiB0b3RhbCBzYWxlcyBvdmVyIHRpbWUgKGRhaWx5KQ0KdmlzX2RhdGVfZGFpbHkgPC0gZ2dwbG90KGRhaWx5X3NhbGVzLCBhZXMoeCA9IERhdGVPbmx5LCB5ID0gVG90YWxTYWxlcykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImdyZWVuIiwgbGluZXdpZHRoID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgU2FsZXMgT3ZlciBUaW1lIChEYWlseSkiLCB4ID0gIkRhdGUiLCB5ID0gIlRvdGFsIFNhbGVzIChSZXZlbnVlKSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfZGF0ZV9kYWlseSkNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgcGxvdCBzaG93cyBmbHVjdHVhdGlvbnMgaW4gZGFpbHkgc2FsZXMsIHdpdGggbm90aWNlYWJsZSBwZWFrcyBkdXJpbmcgY2VydGFpbiBwZXJpb2RzLg0KIyBJbnRlcnByZXRhdGlvbjogU2FsZXMgbWF5IGJlIGluZmx1ZW5jZWQgYnkgZmFjdG9ycyBzdWNoIGFzIHByb21vdGlvbnMsIGhvbGlkYXlzLCBvciBzZWFzb25hbCB0cmVuZHMsIHdpdGggcGVha3MgcG90ZW50aWFsbHkgY29ycmVzcG9uZGluZyB0byBoaWdoLWRlbWFuZCBwZXJpb2RzLg0KDQpgYGB7cn0NCiMgQWdncmVnYXRlIHRoZSBkYXRhIGJ5IG1vbnRoIHRvIGNhbGN1bGF0ZSB0b3RhbCBzYWxlcyBwZXIgbW9udGgNCm1vbnRobHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBtdXRhdGUoTW9udGggPSBmb3JtYXQoSW52b2ljZURhdGUsICIlWS0lbSIpKSAlPiUNCiAgZ3JvdXBfYnkoTW9udGgpICU+JQ0KICBzdW1tYXJpc2UoVG90YWxTYWxlcyA9IHN1bShUb3RhbFByaWNlKSkNCg0KIyBDb252ZXJ0ICdNb250aCcgaW50byBhIGRhdGUgYnkgYXBwZW5kaW5nICctMDEnIHRvIGVhY2ggdmFsdWUNCm1vbnRobHlfc2FsZXMkTW9udGggPC0gYXMuRGF0ZShwYXN0ZTAobW9udGhseV9zYWxlcyRNb250aCwgIi0wMSIpKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbGluZSBwbG90IG9mIHRvdGFsIHNhbGVzIG92ZXIgdGltZSAobW9udGhseSkNCnZpc19kYXRlX21vbnRobHkgPC0gZ2dwbG90KG1vbnRobHlfc2FsZXMsIGFlcyh4ID0gTW9udGgsIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iLCBzaXplID0gMC41KSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBPdmVyIFRpbWUgKE1vbnRobHkpIiwgeCA9ICJNb250aCIsIHkgPSAiVG90YWwgU2FsZXMgKFJldmVudWUpIikgKw0KICB0aGVtZV9saW5lZHJhdygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfZGF0ZV9tb250aGx5KQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtb250aGx5IHNhbGVzIHBsb3Qgc21vb3RocyBvdXQgdGhlIGRhaWx5IGZsdWN0dWF0aW9ucywgc2hvd2luZyBicm9hZGVyIHRyZW5kcyBhbmQgcG90ZW50aWFsIHNlYXNvbmFsaXR5IGluIHRoZSBkYXRhLg0KIyBJbnRlcnByZXRhdGlvbjogVGhlIGRhdGEgaW5kaWNhdGVzIHBlcmlvZHMgb2Ygc3RlYWR5IGdyb3d0aCBhbmQgcG9zc2libGUgc2Vhc29uYWwgc3Bpa2VzLCB1c2VmdWwgZm9yIGZvcmVjYXN0aW5nIGZ1dHVyZSBzYWxlcyBhbmQgbWFuYWdpbmcgaW52ZW50b3J5Lg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbGluZSBwbG90IG9mIHRvdGFsIHNhbGVzIG92ZXIgdGltZSAoZGFpbHkpDQp2aXNfZGF0ZV9kYWlseV9zbSA8LSBnZ3Bsb3QoZGFpbHlfc2FsZXMsIGFlcyh4ID0gRGF0ZU9ubHksIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iLCBzaXplID0gMC41KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAwLjMpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBPdmVyIFRpbWUgKERhaWx5KSIsIHggPSAiRGF0ZSIsIHkgPSAiVG90YWwgU2FsZXMgKFJldmVudWUpIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBDb252ZXJ0IHRvIGFuIGludGVyYWN0aXZlIHBsb3QgdXNpbmcgZ2dwbG90bHkNCmdncGxvdGx5KHZpc19kYXRlX2RhaWx5X3NtKQ0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgbGluZSBwbG90IHdpdGggdGhlIHNtb290aGluZyBsaW5lIHNob3dzIGZsdWN0dWF0aW9ucyBpbiBkYWlseSBzYWxlcywgd2l0aCBzb21lIGRpc3RpbmN0IHBlYWtzLg0KIyBJbnRlcnByZXRhdGlvbjogVGhlIHBlYWtzIGxpa2VseSBjb3JyZXNwb25kIHRvIGhpZ2ggc2FsZXMgZGF5cywgcG9zc2libHkgZHJpdmVuIGJ5IHByb21vdGlvbmFsIGV2ZW50cyBvciBob2xpZGF5cy4gVGhlIHNtb290aCBsaW5lIHByb3ZpZGVzIGFuIG92ZXJhbGwgdHJlbmQsIHNob3dpbmcgZ3Jvd3RoIG9yIGRlY2xpbmUgcGF0dGVybnMgaW4gc2FsZXMgb3ZlciB0aW1lLg0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgZGF5IG9mIHRoZSB3ZWVrIChlLmcuLCBTdW5kYXksIE1vbmRheSkNCnZpc3VhbGl6ZV9kZiREYXlPZldlZWsgPC0gd2Vla2RheXModmlzdWFsaXplX2RmJERhdGVPbmx5KQ0KDQojIEFnZ3JlZ2F0ZSBkYXRhIGJ5IGRheSBvZiB0aGUgd2VlayB0byBjYWxjdWxhdGUgdG90YWwgdHJhZmZpYyAocXVhbnRpdHkgc29sZCkNCndlZWtseV90cmFmZmljIDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoRGF5T2ZXZWVrKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsVHJhZmZpYyA9IHN1bShRdWFudGl0eSkpDQoNCiMgRW5zdXJlIHRoZSBkYXlzIG9mIHRoZSB3ZWVrIGFyZSBpbiB0aGUgY29ycmVjdCBvcmRlcg0KZGF5c19vcmRlciA8LSBjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpDQp3ZWVrbHlfdHJhZmZpYyREYXlPZldlZWsgPC0gZmFjdG9yKHdlZWtseV90cmFmZmljJERheU9mV2VlaywgbGV2ZWxzID0gZGF5c19vcmRlcikNCg0KIyBQcmludCB0aGUgYWdncmVnYXRlZCB3ZWVrbHkgdHJhZmZpYyBkYXRhDQpwcmludCh3ZWVrbHlfdHJhZmZpYykNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgYmFyIHBsb3QgdG8gdmlzdWFsaXplIHRyYWZmaWMgYnkgZGF5IG9mIHRoZSB3ZWVrDQp0cmFmZmljX2J5X2RheV9wbG90IDwtIGdncGxvdCh3ZWVrbHlfdHJhZmZpYywgYWVzKHggPSBEYXlPZldlZWssIHkgPSBUb3RhbFRyYWZmaWMsIGZpbGwgPSBEYXlPZldlZWspKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArICAjIEJhciBwbG90DQogIGxhYnModGl0bGUgPSAiVG90YWwgVHJhZmZpYyAoUXVhbnRpdHkgU29sZCkgYnkgRGF5IG9mIHRoZSBXZWVrIiwgeCA9ICJEYXkgb2YgdGhlIFdlZWsiLCB5ID0gIlRvdGFsIFRyYWZmaWMgKFF1YW50aXR5IFNvbGQpIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh0cmFmZmljX2J5X2RheV9wbG90KQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFNhbGVzIHRyYWZmaWMgcGVha3Mgb24gY2VydGFpbiBkYXlzIG9mIHRoZSB3ZWVrLCB3aXRoIG5vdGljZWFibHkgaGlnaGVyIGFjdGl2aXR5Lg0KIyBJbnRlcnByZXRhdGlvbjogQ3VzdG9tZXIgYmVoYXZpb3Igc2hvd3MgdGhhdCBzcGVjaWZpYyBkYXlzIGRyaXZlIG1vcmUgc2FsZXMsIHdoaWNoIGNhbiBoZWxwIGluIG9wdGltaXppbmcgc3RhZmZpbmcsIHByb21vdGlvbnMsIGFuZCBvcGVyYXRpb25zIGZvciB0aG9zZSBoaWdoLXRyYWZmaWMgZGF5cy4NCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGhvdXIgb2YgdGhlIGRheSBmcm9tIHRoZSBJbnZvaWNlRGF0ZQ0KdmlzdWFsaXplX2RmJEhvdXIgPC0gZm9ybWF0KHZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSwgIiVIIikNCg0KIyBBZ2dyZWdhdGUgZGF0YSBieSBob3VyIG9mIHRoZSBkYXkgdG8gY2FsY3VsYXRlIHRvdGFsIHNhbGVzDQpob3VybHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShIb3VyKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsU2FsZXMgPSBzdW0oVG90YWxQcmljZSkpDQoNCiMgUGxvdCB0b3RhbCBzYWxlcyBieSBob3VyDQp2aXNfc2FsZXNfYnlfaG91ciA8LSBnZ3Bsb3QoaG91cmx5X3NhbGVzLCBhZXMoeCA9IEhvdXIsIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IEhvdXIgb2YgRGF5IiwgeCA9ICJIb3VyIiwgeSA9ICJUb3RhbCBTYWxlcyAoUmV2ZW51ZSkiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIENvbnZlcnQgdG8gYW4gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX3NhbGVzX2J5X2hvdXIpDQoNCmBgYA0KIyBPYnNlcnZhdGlvbjogU2FsZXMgYWN0aXZpdHkgdmFyaWVzIHRocm91Z2hvdXQgdGhlIGRheSwgd2l0aCBwZWFrcyBhdCBjZXJ0YWluIGhvdXJzLg0KIyBJbnRlcnByZXRhdGlvbjogVGhpcyBhbmFseXNpcyBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIHBlYWsgc2hvcHBpbmcgdGltZXMsIGFsbG93aW5nIHRoZSBzdG9yZSB0byBvcHRpbWl6ZSByZXNvdXJjZXMsIGFkdmVydGlzaW5nLCBhbmQgY3VzdG9tZXIgc2VydmljZSBkdXJpbmcgdGhlIGJ1c2llc3QgcGVyaW9kcy4NCmBgYHtyfQ0KIyBBZ2dyZWdhdGUgc2FsZXMgYnkgZGF0ZSBhbmQgY291bnRyeQ0Kc2FsZXNfYnlfY291bnRyeV9vdmVyX3RpbWUgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShEYXRlT25seSwgQ291bnRyeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIEZpbHRlciB0byB0b3AgNSBjb3VudHJpZXMgZm9yIGJldHRlciBjb21wYXJpc29uDQp0b3BfNV9jb3VudHJpZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsU2FsZXMgPSBzdW0oVG90YWxQcmljZSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoVG90YWxTYWxlcykpICU+JQ0KICBoZWFkKDUpDQoNCiMgRmlsdGVyIGRhdGEgZm9yIHRoZSB0b3AgNSBjb3VudHJpZXMNCnNhbGVzX3RvcF81X2NvdW50cmllcyA8LSBzYWxlc19ieV9jb3VudHJ5X292ZXJfdGltZSAlPiUNCiAgZmlsdGVyKENvdW50cnkgJWluJSB0b3BfNV9jb3VudHJpZXMkQ291bnRyeSkNCg0KIyBQbG90IHNhbGVzIHRyZW5kcyBieSBjb3VudHJ5IG92ZXIgdGltZQ0KdmlzX3NhbGVzX2J5X2NvdW50cnlfdGltZSA8LSBnZ3Bsb3Qoc2FsZXNfdG9wXzVfY291bnRyaWVzLCBhZXMoeCA9IERhdGVPbmx5LCB5ID0gVG90YWxTYWxlcywgY29sb3IgPSBDb3VudHJ5KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2FsZXMgVHJlbmRzIE92ZXIgVGltZSBieSBDb3VudHJ5IiwgeCA9ICJEYXRlIiwgeSA9ICJUb3RhbCBTYWxlcyAoUmV2ZW51ZSkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIENvbnZlcnQgdG8gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX3NhbGVzX2J5X2NvdW50cnlfdGltZSkNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBTYWxlcyB0cmVuZHMgdmFyeSBhY3Jvc3MgdGhlIHRvcCA1IGNvdW50cmllcywgd2l0aCBjZXJ0YWluIHBlcmlvZHMgc2hvd2luZyBzeW5jaHJvbml6ZWQgcGVha3MgYWNyb3NzIG11bHRpcGxlIGNvdW50cmllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoaXMgYW5hbHlzaXMgcmV2ZWFscyBnbG9iYWwgc2FsZXMgdHJlbmRzIGFuZCBoaWdobGlnaHRzIHRoZSBtb3N0IGFjdGl2ZSBtYXJrZXRzIGR1cmluZyBzcGVjaWZpYyBwZXJpb2RzLiBJbnNpZ2h0cyBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSBjb3VudHJ5LXNwZWNpZmljIGRlbWFuZCBjeWNsZXMgYW5kIGFkanVzdCBpbnZlbnRvcnkgb3IgbWFya2V0aW5nIGVmZm9ydHMgYWNjb3JkaW5nbHkuDQoNCmBgYHtyfQ0KIyBDYXRlZ29yaXplIGN1c3RvbWVycyBiYXNlZCBvbiB0b3RhbCBzcGVuZGluZw0KY3VzdG9tZXJfc2VnbWVudHMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDdXN0b21lci5JRCkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNwZW50ID0gc3VtKFRvdGFsUHJpY2UpKSAlPiUNCiAgbXV0YXRlKFNlZ21lbnQgPSBjYXNlX3doZW4oDQogICAgVG90YWxTcGVudCA8IDEwMCB+ICJMb3cgU3BlbmRlcnMiLA0KICAgIFRvdGFsU3BlbnQgPj0gMTAwICYgVG90YWxTcGVudCA8IDUwMCB+ICJNZWRpdW0gU3BlbmRlcnMiLA0KICAgIFRvdGFsU3BlbnQgPj0gNTAwIH4gIkhpZ2ggU3BlbmRlcnMiDQogICkpDQoNCiMgVmlzdWFsaXplIHRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggc2VnbWVudA0KdmlzX2N1c3RvbWVyX3NlZ21lbnRzIDwtIGdncGxvdChjdXN0b21lcl9zZWdtZW50cywgYWVzKHggPSBTZWdtZW50LCBmaWxsID0gU2VnbWVudCkpICsNCiAgZ2VvbV9iYXIoKSArDQogIGxhYnModGl0bGUgPSAiQ3VzdG9tZXIgU2VnbWVudHMgQmFzZWQgb24gVG90YWwgU3BlbmRpbmciLCB4ID0gIlNlZ21lbnQiLCB5ID0gIk51bWJlciBvZiBDdXN0b21lcnMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIENvbnZlcnQgdG8gYW4gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX2N1c3RvbWVyX3NlZ21lbnRzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgYXJlIGxvdyBzcGVuZGVycywgd2l0aCBmZXdlciBtZWRpdW0gYW5kIGhpZ2ggc3BlbmRlcnMuDQojIEludGVycHJldGF0aW9uOiBUaGUgc3RvcmUgaGFzIGEgYnJvYWQgY3VzdG9tZXIgYmFzZSwgYnV0IG1vc3Qgb2YgdGhlIHJldmVudWUgaXMgbGlrZWx5IGNvbWluZyBmcm9tIGEgc21hbGwgbnVtYmVyIG9mIGhpZ2gtc3BlbmRpbmcgY3VzdG9tZXJzLiBSZXRhaW5pbmcgaGlnaCBzcGVuZGVycyBhbmQgY29udmVydGluZyBtb3JlIGxvdyBzcGVuZGVycyB0byBtZWRpdW0gb3IgaGlnaCBzcGVuZGVycyBzaG91bGQgYmUgYSBwcmlvcml0eS4NCg0KYGBge3J9DQojIENvdW50IHRoZSBudW1iZXIgb2Ygb3JkZXJzIHBlciBwcm9kdWN0IChpbnN0ZWFkIG9mIHRvdGFsIHF1YW50aXR5IHNvbGQpDQp0b3BfcHJvZHVjdHNfb3JkZXJzIDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoU3RvY2tDb2RlLCBEZXNjcmlwdGlvbikgJT4lDQogIHN1bW1hcmlzZShPcmRlckNvdW50ID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKE9yZGVyQ291bnQpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IHRvcCBwcm9kdWN0cyBiYXNlZCBvbiBudW1iZXIgb2Ygb3JkZXJzDQp2aXNfdG9wX3Byb2R1Y3RzX29yZGVycyA8LSBnZ3Bsb3QodG9wX3Byb2R1Y3RzX29yZGVycywgYWVzKHggPSByZW9yZGVyKERlc2NyaXB0aW9uLCBPcmRlckNvdW50KSwgeSA9IE9yZGVyQ291bnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIm9yYW5nZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgUHJvZHVjdHMgQmFzZWQgb24gTnVtYmVyIG9mIE9yZGVycyIsIHggPSAiUHJvZHVjdCBEZXNjcmlwdGlvbiIsIHkgPSAiTnVtYmVyIG9mIE9yZGVycyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX3Byb2R1Y3RzX29yZGVycykNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBBIGZldyBwcm9kdWN0cyBhcmUgb3JkZXJlZCBtdWNoIG1vcmUgZnJlcXVlbnRseSB0aGFuIG90aGVycy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIHByb2R1Y3RzIGFyZSBwb3B1bGFyIHdpdGggY3VzdG9tZXJzIGFuZCBsaWtlbHkgcmVwcmVzZW50IGVzc2VudGlhbCBvciBmYXN0LXNlbGxpbmcgaXRlbXMuIFRoZSBzdG9yZSBtYXkgYmVuZWZpdCBmcm9tIHN0b2NraW5nIHRoZXNlIGl0ZW1zIGluIGxhcmdlciBxdWFudGl0aWVzIGFuZCBwcm9tb3RpbmcgdGhlbSBtb3JlIHRvIG1haW50YWluIGFuZCBncm93IHNhbGVzLg0K